using System;
using System.Windows.Forms;
using System.Globalization;
using System.IO;
using System.Collections;
using System.Net;
using RapidSpider.Views;
using RapidSpider.Enumerators;
using System.Diagnostics;
using RapidSpider.DownloadAgents;
using System.Deployment.Application;
using System.ComponentModel;
using RapidSpider.DownloadInitialisers;

namespace RapidSpider.ApplicationControllers
{
    public partial class Controller
    {
        #region Members
        private MainView _MainView;
        #endregion

        #region Methods
        private void MainView_Show()
        {
            // Instantiate an instance
            this._MainView = new MainView();

            this._MainView.comboboxURI.TextChanged += 
                new EventHandler(MainView_comboboxURI_TextChanged);
            this._MainView.comboboxURI.KeyUp +=
                new KeyEventHandler(MainView_comboboxURI_KeyUp);
            this._MainView.buttonOpenFileDialog.Click += 
                new EventHandler(MainView_buttonOpenFileDialog_Click);
            this._MainView.buttonAddDownload.Click += 
                new EventHandler(MainView_buttonAddDownload_Click);
            this._MainView.buttonReinitialise.Click += 
                new EventHandler(MainView_buttonReinitialise_Click);
            this._MainView.buttonNavigateURI.Click += 
                new EventHandler(MainView_buttonNavigateURI_Click);
            this._MainView.buttonClearHistory.Click +=
                new EventHandler(MainView_buttonClearHistory_Click);

            this._MainView.buttonPlayAll.Click += 
                new EventHandler(MainView_buttonPlayAll_Click);
            this._MainView.buttonPauseAll.Click += 
                new EventHandler(MainView_buttonPauseAll_Click);
            this._MainView.buttonStopAll.Click += 
                new EventHandler(MainView_buttonStopAll_Click);

            this._MainView.buttonSettings.Click += 
                new EventHandler(MainView_buttonSettings_Click);
            this._MainView.buttonOpenTargetFolder.Click += 
                new EventHandler(MainView_buttonOpenTargetFolder_Click);
            this._MainView.buttonShowLogs.Click += 
                new EventHandler(MainView_buttonShowLogs_Click);

            this._MainView.buttonCheckForUpdates.Click += 
                new EventHandler(MainView_buttonCheckForUpdates_Click);

            this._MainView.gridDownloads.MouseUp += 
                new MouseEventHandler(MainView_gridDownloads_MouseUp);
            this._MainView.menuitemPlaySelected.Click += 
                new EventHandler(MainView_menuitemPlaySelected_Click);
            this._MainView.menuitemPauseSelected.Click += 
                new EventHandler(MainView_menuitemPauseSelected_Click);
            this._MainView.menuitemResumeSelected.Click += 
                new EventHandler(MainView_menuitemResumeSelected_Click);
            this._MainView.menuitemMoveUp.Click += 
                new EventHandler(MainView_menuitemMoveUp_Click);
            this._MainView.menuitemMoveDown.Click += 
                new EventHandler(MainView_menuitemMoveDown_Click);
            this._MainView.menuitemDeleteSelected.Click += 
                new EventHandler(MainView_menuitemDeleteSelected_Click);

            this._MainView.NotifyIcon.DoubleClick += 
                new EventHandler(MainView_NotifyIcon_DoubleClick);
            this._MainView.menuitemCheckForUpdates.Click += 
                new EventHandler(MainView_menuitemCheckForUpdates_Click);
            this._MainView.menuitemPlayAll.Click +=
                new EventHandler(MainView_menuitemPlayAll_Click);
            this._MainView.menuitemPauseAll.Click +=
                new EventHandler(MainView_menuitemPauseAll_Click);
            this._MainView.menuitemStopAll.Click +=
                new EventHandler(MainView_menuitemStopAll_Click);
            this._MainView.menuitemExit.Click += 
                new EventHandler(MainView_menuitemExit_Click);

            this._MainView.Resize += 
                new EventHandler(MainView_Resize);
            this._MainView.FormClosing += 
                new FormClosingEventHandler(MainView_FormClosing);
            this._MainView.StatusChanged += 
                new EventHandler(MainView_StatusChanged);
            
            this._MainView.labelVersion.Text =
                Controller.PublishVersion;

            this._MainView.RepopulateURIComboBox();

            this._MainView.TopMost =
                RegistryAgent.AlwaysOnTop;

            // Attach the MainView to the Application Context
            this._AppContext.MainForm = this._MainView;

            // Show the form
            this._MainView.Show();

            this.UpdateNotificationView_Show();
        }

        #region Event Handlers
        private void MainView_buttonCheckForUpdates_Click(object sender, EventArgs e)
        {
            Application.DoEvents();

            if (ApplicationDeployment.IsNetworkDeployed)
            {
                try
                {
                    this._MainView.Cursor =
                        Cursors.WaitCursor;

                    Application.DoEvents();

                    bool IsUpdateAvailable = 
                        ApplicationDeployment.CurrentDeployment.CheckForUpdate();

                    if (IsUpdateAvailable)
                    {
                        switch (MessageBox.Show(
                            this._MainView,
                            "An update is available\n\n" +
                                "Do you want to download it now?",
                            this._MainView.Text,
                            MessageBoxButtons.YesNo,
                            MessageBoxIcon.Question,
                            MessageBoxDefaultButton.Button1))
                        {
                            case DialogResult.No:
                                this._MainView.Cursor =
                                    Cursors.Default;

                                Application.DoEvents();
                                return;
                        }

                        Application.DoEvents();

                        ApplicationDeployment.CurrentDeployment.UpdateCompleted +=
                            new AsyncCompletedEventHandler(MainView_CurrentDeployment_UpdateCompleted);

                        ApplicationDeployment.CurrentDeployment.UpdateAsync();
                    }
                    else
                    {
                        MessageBox.Show(
                            this._MainView,
                            "There are no updates available at this time",
                            this._MainView.Text,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information,
                            MessageBoxDefaultButton.Button1);

                        Application.DoEvents();
                    }
                }
                finally
                {
                    this._MainView.Cursor =
                        Cursors.Default;
                }
            }
        }

        private void MainView_menuitemCheckForUpdates_Click(
            object sender, 
            EventArgs e)
        {
            this._MainView.buttonCheckForUpdates.PerformClick();
        }

        private void MainView_CurrentDeployment_UpdateCompleted(
            object sender,
            AsyncCompletedEventArgs e)
        {
            if (null != e.Error)
            {
                throw e.Error;
            }

            switch (MessageBox.Show(
                this._MainView,
                "The application has been upgraded\n\nDo you want to restart now?",
                "Update Notification",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Warning,
                MessageBoxDefaultButton.Button1))
            {
                case DialogResult.No:
                    Application.DoEvents();
                    return;
            }

            Application.DoEvents();

            Application.Restart();
        }

        private void MainView_menuitemMoveDown_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (this._MainView.gridDownloads.SelectedRows.Count.Equals(1) &&
                !this._MainView.gridDownloads.CurrentRow.Index.Equals(
                    this._MainView.gridDownloads.Rows.Count - 1))
            {
                int theSelectedRowIndex =
                    this._MainView.gridDownloads.CurrentRow.Index;
                DataGridViewRow theSelectedRow =
                    this._MainView.gridDownloads.CurrentRow;

                IDownloadAgent theNextAgent =
                    this._MainView.gridDownloads.Rows[
                        theSelectedRowIndex + 1].Tag as IDownloadAgent;

                if (!theNextAgent.Status.Equals(DownloadAgentStatus.Starting) &&
                    !theNextAgent.Status.Equals(DownloadAgentStatus.Downloading))
                {
                    this._MainView.gridDownloads.Rows.Remove(
                        theSelectedRow);
                    this._MainView.gridDownloads.Rows.Insert(
                        theSelectedRowIndex + 1,
                        theSelectedRow);

                    this._MainView.gridDownloads.ClearSelection();

                    this._MainView.gridDownloads.CurrentCell =
                        this._MainView.gridDownloads.Rows[
                            theSelectedRowIndex + 1].Cells[0];
                    this._MainView.gridDownloads.Rows[
                        theSelectedRowIndex + 1].Selected = true;
                }
            }
        }

        private void MainView_menuitemMoveUp_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (this._MainView.gridDownloads.SelectedRows.Count.Equals(1) &&
                !this._MainView.gridDownloads.CurrentRow.Index.Equals(0))
            {
                int theSelectedRowIndex =
                    this._MainView.gridDownloads.CurrentRow.Index;
                DataGridViewRow theSelectedRow =
                    this._MainView.gridDownloads.CurrentRow;

                IDownloadAgent thePreviousAgent =
                    this._MainView.gridDownloads.Rows[
                        theSelectedRowIndex - 1].Tag as IDownloadAgent;

                if (!thePreviousAgent.Status.Equals(DownloadAgentStatus.Starting) &&
                    !thePreviousAgent.Status.Equals(DownloadAgentStatus.Downloading))
                {
                    this._MainView.gridDownloads.Rows.Remove(
                        theSelectedRow);
                    this._MainView.gridDownloads.Rows.Insert(
                        theSelectedRowIndex - 1,
                        theSelectedRow);

                    this._MainView.gridDownloads.ClearSelection();

                    this._MainView.gridDownloads.CurrentCell =
                        this._MainView.gridDownloads.Rows[
                            theSelectedRowIndex - 1].Cells[0];
                    this._MainView.gridDownloads.Rows[
                        theSelectedRowIndex - 1].Selected = true;
                }
            }
        }

        private void MainView_buttonReinitialise_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Refresh();

            if (this._MainView.gridDownloads.Rows.Count > 0)
            {
                this._MainView.gridDownloads.ClearSelection();

                this.InitialiseAll();
            }
        }

        private void MainView_buttonShowLogs_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.OpenFileDialog.Filter =
                "Log files (*.log)|*.log";
            this._MainView.OpenFileDialog.InitialDirectory =
                RegistryAgent.LogFilePath;

            switch (this._MainView.OpenFileDialog.ShowDialog(this._MainView))
            {
                case DialogResult.OK:
                    Process.Start(this._MainView.OpenFileDialog.FileName);
                    break;
            }
        }

        private void MainView_buttonNavigateURI_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            try
            {
                Uri theUri = new Uri(
                    this._MainView.comboboxURI.Text);

                Process.Start(theUri.ToString());
            }
            catch (UriFormatException) { }
        }

        private void MainView_buttonClearHistory_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            switch (MessageBox.Show(
                this._MainView,
                "Are you sure you want to clear the download history and log files?",
                this._MainView.Text,
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button2))
            {
                case DialogResult.No:
                    return;
            }

            RegistryAgent.ClearDownloadHistory();

            foreach (string thisLogFile in
                Directory.GetFiles(RegistryAgent.LogFilePath, "*.log"))
            {
                File.Delete(thisLogFile);
            }

            this._MainView.RepopulateURIComboBox();

            this._MainView.buttonClearHistory.Enabled = false;
        }

        private void MainView_Resize(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (this._MainView.WindowState.Equals(
                FormWindowState.Minimized))
            {
                this._MainView.NotifyIcon.Visible = true;
                this._MainView.Hide();
            }

            this._MainView.gridDownloads.ClearSelection();
            this._MainView.Refresh();
        }

        private void MainView_NotifyIcon_DoubleClick(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.NotifyIcon.Visible = false;
            this._MainView.Show();
            this._MainView.WindowState = FormWindowState.Normal;
        }

        private void MainView_menuitemStopAll_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Status = MainViewStatus.Stopped;
        }

        private void MainView_menuitemPauseAll_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Status = MainViewStatus.Paused;
        }

        private void MainView_menuitemPlayAll_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Status = MainViewStatus.Started;
        }

        private void MainView_menuitemExit_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (this._MainView.gridDownloads.Rows.Count > 0)
            {
                switch (MessageBox.Show(
                    this._MainView,
                    "Are you sure you want to exit?",
                    this._MainView.Text,
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question,
                    MessageBoxDefaultButton.Button2))
                {
                    case DialogResult.No:
                        return;
                }
            }

            Controller.AbortAndRemoveAllThreads();
            
            Application.Exit();
        }

        private void MainView_comboboxURI_TextChanged(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            ToolStripComboBox theURIComboBox = sender as ToolStripComboBox;

            this._MainView.buttonAddDownload.Enabled =
                theURIComboBox.Text.Length > 0 &&
                !theURIComboBox.Text.Equals("<Type or Paste URI or Path here>");

            this._MainView.buttonNavigateURI.Enabled = 
                Uri.IsWellFormedUriString(
                    this._MainView.comboboxURI.Text, 
                    UriKind.RelativeOrAbsolute);
        }

        private void MainView_comboboxURI_KeyUp(
            object sender, 
            KeyEventArgs e)
        {
            Application.DoEvents();

            if (e.KeyCode.Equals(Keys.Enter))
            {
                this.MainView_buttonAddDownload_Click(
                    this._MainView.buttonAddDownload,
                    new EventArgs());
            }
        }

        private void MainView_buttonOpenFileDialog_Click(
            object sender,
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.OpenFileDialog.Filter =
                "All files|*.*";
            this._MainView.OpenFileDialog.InitialDirectory =
                Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            switch (this._MainView.OpenFileDialog.ShowDialog(this._MainView))
            {
                case DialogResult.OK:
                    this._MainView.comboboxURI.Text =
                        this._MainView.OpenFileDialog.FileName;
                    break;
            }
        }
        
        private void MainView_buttonAddDownload_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Cursor = Cursors.WaitCursor;
            this._MainView.buttonAddDownload.Enabled = false;
            this._MainView.buttonReinitialise.Enabled = false;

            for (int i = this._MainView.gridDownloads.Rows.Count - 1; i > -1; i--)
            {
                IDownloadAgent thisAgent =
                    this._MainView.gridDownloads.Rows[i].Tag
                        as IDownloadAgent;

                if (!thisAgent.Status.Equals(DownloadAgentStatus.Downloading) &&
                    !thisAgent.Status.Equals(DownloadAgentStatus.Initialised))
                {
                    this._MainView.gridDownloads.Rows.Remove(
                        this._MainView.gridDownloads.Rows[i]);
                }
            }

            ArrayList theURIList = new ArrayList();

            try
            {
                string theURI = this._MainView.comboboxURI.Text.Trim();

                if (!RegistryAgent.DownloadHistory.Contains(theURI))
                {
                    RegistryAgent.DownloadHistory.PushFront(theURI);

                    if (RegistryAgent.DownloadHistory.Count > 
                        RegistryAgent.DOWNLOAD_HISTORY_SIZE)
                    {
                        RegistryAgent.DownloadHistory.PopBack();
                    }

                    RegistryAgent.SaveDownloadHistory();

                    this._MainView.RepopulateURIComboBox();
                }

                if (IsURI(this._MainView.comboboxURI.Text))
                {
                    this.AppendLogFile("AddFromURI", theURI);

                    theURIList.AddRange(
                        DownloadInitialiser.GenerateFileList(
                            new Uri(this._MainView.comboboxURI.Text)));
                }
                else
                {
                    this.AppendLogFile("AddFromFile", theURI);

                    theURIList.AddRange(
                        DownloadInitialiser.GenerateFileList(
                            new FileInfo(this._MainView.comboboxURI.Text)));
                }
            }
            catch (UriFormatException)
            {
                MessageBox.Show(
                    this._MainView,
                    "The URI you have a supplied in invalid",
                    this._MainView.Text,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Stop,
                    MessageBoxDefaultButton.Button1);
            }
            catch (WebException theException)
            {
                MessageBox.Show(
                    this._MainView,
                    String.Format(
                        CultureInfo.CurrentCulture,
                        "There was a problem connecting to the supplied server:\n\n{0}",
                        theException.Message),
                    this._MainView.Text,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Stop,
                    MessageBoxDefaultButton.Button1);
            }
            finally
            {
                this._MainView.Cursor = Cursors.Default;
                this._MainView.buttonAddDownload.Enabled = true;
            }

            ExtendedDialogResult theExtendedDialogResult =
                ExtendedDialogResult.NotSet;

            Application.DoEvents();

            foreach (Uri thisURI in theURIList)
            {
                try
                {
                    if (!this.IsAlreadyBeingDownloaded(thisURI))
                    {
                        DirectoryInfo theTargetDirectory = new DirectoryInfo(RegistryAgent.DownloadPath);
                        long theDownloadedAmount = 0;

                        if (thisURI.Segments[thisURI.Segments.Length - 1].Contains("."))
                        {
                            FileInfo theTargetFilePath = new FileInfo(thisURI.LocalPath);

                            FileInfo theTargetFile = new FileInfo(
                                theTargetDirectory.FullName + "\\" +
                                theTargetFilePath.Name);

                            FileInfo theIncompleteTargetFile = new FileInfo(
                                theTargetDirectory.FullName + "\\" +
                                theTargetFilePath.Name + ".incomplete");

                            int thisIndex = 0;

                            if (theTargetFile.Exists)
                            {
                                if (theExtendedDialogResult.Equals(ExtendedDialogResult.NotSet) ||
                                    theExtendedDialogResult.Equals(ExtendedDialogResult.Append) ||
                                    theExtendedDialogResult.Equals(ExtendedDialogResult.Ignore) ||
                                    theExtendedDialogResult.Equals(ExtendedDialogResult.Overwrite))
                                {
                                    decimal theTargetFileLength =
                                        Convert.ToDecimal(theTargetFile.Length, CultureInfo.CurrentCulture);

                                    theExtendedDialogResult =
                                        this.ConfirmationDialogView_Show(
                                            this._MainView,
                                            String.Format(
                                                CultureInfo.CurrentCulture,
                                                "File '{0}' already exists with a size of {1}Mb\n\n" +
                                                "What do you want to do?",
                                                theTargetFile.Name,
                                                Math.Round(theTargetFileLength / 1048576, 2)));

                                    Application.DoEvents();
                                }

                                switch (theExtendedDialogResult)
                                {
                                    case ExtendedDialogResult.Overwrite:
                                    case ExtendedDialogResult.OverwriteAll:
                                        this.AppendLogFile("SetToOverwrite", thisURI.ToString());

                                        File.Delete(theTargetFile.FullName);

                                        thisIndex =
                                            this._MainView.AddRow(theTargetFile, String.Empty);
                                        break;
                                    case ExtendedDialogResult.Append:
                                    case ExtendedDialogResult.AppendAll:
                                        this.AppendLogFile("SetToResume", thisURI.ToString());

                                        theDownloadedAmount = theTargetFile.Length;

                                        thisIndex =
                                            this._MainView.AddRow(theTargetFile, "Set to Resume");
                                        break;
                                    case ExtendedDialogResult.Ignore:
                                    case ExtendedDialogResult.IgnoreAll:
                                        this.AppendLogFile("SetToIgnore", thisURI.ToString());
                                        continue;
                                }
                            }
                            else if (theIncompleteTargetFile.Exists)
                            {
                                this.AppendLogFile("SetToResume", thisURI.ToString());

                                theDownloadedAmount = theIncompleteTargetFile.Length;

                                thisIndex =
                                    this._MainView.AddRow(theTargetFile, "Set to Resume");
                            }
                            else
                            {
                                thisIndex =
                                    this._MainView.AddRow(theTargetFile, String.Empty);
                            }

                            Application.DoEvents();


                            IDownloadAgent thisAgent = null;

                            if (DownloadInitialiser.RapidshareComLinkRegex.IsMatch(thisURI.ToString()))
                            {
                                thisAgent = new RapidshareComDownloadAgent(
                                    thisURI,
                                    theDownloadedAmount,
                                    theTargetDirectory,
                                    RegistryAgent.RapidshareComLogin,
                                    RegistryAgent.RapidshareComPassword,
                                    true,
                                    RegistryAgent.BufferSize,
                                    RegistryAgent.Timeout * 1000);
                            }
                            else if (DownloadInitialiser.HotfileComLinkRegex.IsMatch(thisURI.ToString()))
                            {
                                thisAgent = new HotfileComDownloadAgent(
                                    thisURI,
                                    theDownloadedAmount,
                                    theTargetDirectory,
                                    RegistryAgent.HotfileComLogin,
                                    RegistryAgent.HotfileComPassword,
                                    true,
                                    RegistryAgent.BufferSize,
                                    RegistryAgent.Timeout * 1000);
                            }

                            if (null != thisAgent)
                            {
                                thisAgent.Initialising +=
                                    new EventHandler(DownloadAgent_Initialising);
                                thisAgent.Initialised +=
                                    new EventHandler(DownloadAgent_Initialised);
                                thisAgent.Starting +=
                                    new EventHandler(DownloadAgent_Starting);
                                thisAgent.Downloading +=
                                    new EventHandler(DownloadAgent_Downloading);
                                thisAgent.Paused +=
                                    new EventHandler(DownloadAgent_Paused);
                                thisAgent.Failed +=
                                    new EventHandler(DownloadAgent_Failed);
                                thisAgent.Completed +=
                                    new EventHandler(DownloadAgent_Completed);
                                thisAgent.Cancelled +=
                                    new EventHandler(DownloadAgent_Cancelled);
                                thisAgent.StatusChanged +=
                                    new EventHandler(DownloadAgent_StatusChanged);
                            }

                            this._MainView.gridDownloads.Rows[thisIndex].Tag = thisAgent;
                        }
                    }
                }
                catch (ArgumentException theException)
                {
                    switch (theException.Message)
                    {
                        case "Target Directory not supplied!":
                            MessageBox.Show(
                                this._MainView,
                                "The Target Directory does not exist",
                                this._MainView.Text,
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Stop,
                                MessageBoxDefaultButton.Button1);

                            this._MainView.gridDownloads.Rows.Clear();

                            this.SettingsView_Show(this._MainView);
                            return;
                    }
                }
                catch (UriFormatException)
                {
                    continue;
                }
                catch (NotSupportedException)
                {
                    continue;
                }
                catch (PathTooLongException)
                {
                    continue;
                }
            }

            this._MainView.Refresh();

            if (this._MainView.gridDownloads.Rows.Count > 0)
            {
                this._MainView.gridDownloads.ClearSelection();

                this.InitialiseAll();

                if (this._MainView.Status.Equals(MainViewStatus.Started))
                {
                    this.PlayAll();
                }
                else
                {
                    this._MainView.buttonPlayAll.Enabled = true;
                }
            }

            this._MainView.Cursor = Cursors.Default;
        }

        private void MainView_buttonPlayAll_Click(
            object sender,
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Status = MainViewStatus.Started;
        }

        private void MainView_buttonPauseAll_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Status = MainViewStatus.Paused;
        }

        private void MainView_buttonStopAll_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this._MainView.Status = MainViewStatus.Stopped;
        }
        
        private void MainView_buttonSettings_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            this.SettingsView_Show(this._MainView);
        }

        private void MainView_buttonOpenTargetFolder_Click(object sender, EventArgs e)
        {
            Application.DoEvents();

            Process.Start(
                "explorer.exe",
                String.Format(
                    CultureInfo.CurrentCulture,
                    "/e,{0}",
                    RegistryAgent.DownloadPath));
        }

        private void MainView_gridDownloads_MouseUp(
            object sender, 
            MouseEventArgs e)
        {
            Application.DoEvents();

            DataGridView theDownloadsGrid = sender as DataGridView;
            MainView theParentView = theDownloadsGrid.FindForm() as MainView;

            theParentView.menuitemPlaySelected.Enabled =
                theParentView.menuitemPauseSelected.Enabled =
                theParentView.menuitemDeleteSelected.Enabled =
                theParentView.menuitemMoveUp.Enabled =
                theParentView.menuitemMoveDown.Enabled =
                    false;

            if (null != theDownloadsGrid.CurrentRow)
            {
                int theSelectedRowIndex =
                    theDownloadsGrid.CurrentRow.Index;

                IDownloadAgent theSelectedAgent =
                    theDownloadsGrid.CurrentRow.Tag as IDownloadAgent;

                theParentView.menuitemPlaySelected.Enabled =
                    this.ActiveDownloadCount < RegistryAgent.ConcurrentDownloads &&
                        (theSelectedAgent.Status.Equals(DownloadAgentStatus.Initialised) ||
                        theSelectedAgent.Status.Equals(DownloadAgentStatus.Paused) ||
                        theSelectedAgent.Status.Equals(DownloadAgentStatus.Failed));

                theParentView.menuitemPauseSelected.Enabled =
                    theSelectedAgent.Status.Equals(DownloadAgentStatus.Downloading);

                theParentView.menuitemResumeSelected.Enabled =
                    theSelectedAgent.Status.Equals(DownloadAgentStatus.TimedOut);

                theParentView.menuitemDeleteSelected.Enabled =
                    !theSelectedAgent.Status.Equals(DownloadAgentStatus.Downloading);

                if (!theSelectedRowIndex.Equals(0))
                {
                    IDownloadAgent thePreviousAgent =
                        theDownloadsGrid.Rows[theSelectedRowIndex - 1].Tag
                            as IDownloadAgent;

                    theParentView.menuitemMoveUp.Enabled =
                        !thePreviousAgent.Status.Equals(DownloadAgentStatus.Starting) &&
                        !thePreviousAgent.Status.Equals(DownloadAgentStatus.Downloading);
                }

                if (!theSelectedRowIndex.Equals(theDownloadsGrid.Rows.Count - 1))
                {
                    IDownloadAgent theNextAgent =
                        theDownloadsGrid.Rows[theSelectedRowIndex + 1].Tag
                            as IDownloadAgent;

                    theParentView.menuitemMoveDown.Enabled =
                        !theNextAgent.Status.Equals(DownloadAgentStatus.Starting) &&
                        !theNextAgent.Status.Equals(DownloadAgentStatus.Downloading);
                }
            }
        }

        private void MainView_menuitemPlaySelected_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (null != this._MainView.SelectedRow)
            {
                IDownloadAgent thisAgent =
                    this._MainView.SelectedRow.Tag as IDownloadAgent;

                this.StartDownload(thisAgent);
            }
        }

        private void MainView_menuitemPauseSelected_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (null != this._MainView.SelectedRow)
            {
                IDownloadAgent thisAgent =
                    this._MainView.SelectedRow.Tag as IDownloadAgent;

                thisAgent.Status = DownloadAgentStatus.Paused;
            }
        }

        private void MainView_menuitemResumeSelected_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            if (null != this._MainView.SelectedRow)
            {
                IDownloadAgent thisAgent =
                    this._MainView.SelectedRow.Tag as IDownloadAgent;

                thisAgent.Status = DownloadAgentStatus.Initialised;
            }
        }

        private void MainView_menuitemDeleteSelected_Click(
            object sender, 
            EventArgs e)
        {
            Application.DoEvents();

            for (int i = this._MainView.gridDownloads.SelectedRows.Count - 1; i > -1; i--)
            {
                IDownloadAgent thisAgent =
                    this._MainView.gridDownloads.SelectedRows[i].Tag
                        as IDownloadAgent;

                if (!thisAgent.Status.Equals(DownloadAgentStatus.Downloading))
                {
                    this.AppendLogFile("Removed", thisAgent.SourceUri.ToString());

                    this._MainView.gridDownloads.Rows.Remove(
                        this._MainView.gridDownloads.SelectedRows[i]);
                }
            }

            if (this._MainView.Status.Equals(MainViewStatus.Started))
            {
                this.PlayAll();
            }
        }

        private void MainView_FormClosing(
            object sender, 
            FormClosingEventArgs e)
        {
            Application.DoEvents();

            if (e.CloseReason.Equals(CloseReason.UserClosing))
            {
                MainView theParentView = sender as MainView;

                theParentView.WindowState = FormWindowState.Minimized;

                e.Cancel = true;
                return;
            }

            Controller.AbortAndRemoveAllThreads();
        }

        private void MainView_StatusChanged(
            object sender,
            EventArgs e)
        {
            Application.DoEvents();

            MainView theParentView =
                sender as MainView;

            if (null != theParentView)
            {
                switch (theParentView.Status)
                {
                    case MainViewStatus.Paused:
                        this.PauseAll();
                        break;
                    case MainViewStatus.Started:
                        this.PlayAll();
                        break;
                    default:
                        this.StopAll();
                        break;
                }
            }
        }
        #endregion

        #region Private Helpers
        private bool IsAlreadyBeingDownloaded(
            Uri theUri) 
        {
            foreach (DataGridViewRow thisRow in
                this._MainView.gridDownloads.Rows)
            {
                IDownloadAgent thisAgent =
                    thisRow.Tag as IDownloadAgent;

                FileInfo thisUriFileInfo =
                    new FileInfo(theUri.LocalPath);

                if (thisAgent.IncompleteTargetFileInfo.Name
                    .Equals(thisUriFileInfo.Name))
                {
                    return true;
                }
            }

            return false;
        }

        private void AppendLogFile(
            string theAction,
            string theURI)
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this._MainView.InvokeRequired)
            {
                this._MainView.Invoke(
                    new UpdateLogFileHandler(this.AppendLogFile),
                    theAction,
                    theURI);
            }
            else
            {
                if (RegistryAgent.LogActions)
                {
                    if (!Directory.Exists(RegistryAgent.LogFilePath))
                    {
                        Directory.CreateDirectory(RegistryAgent.LogFilePath);
                    }

                    System.IO.File.AppendAllText(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            "{0}\\RapidSpider_{1}.log",
                            RegistryAgent.LogFilePath,
                            DateTime.Now.ToString(
                                "yyyyMMdd",
                                CultureInfo.CurrentCulture)),
                        String.Format(
                            CultureInfo.CurrentCulture,
                            "[{0} {1}] {2} - {3}{4}",
                            DateTime.Now.ToString(
                                "yyyy-MMM-dd",
                                CultureInfo.CurrentCulture),
                            DateTime.Now.ToString(
                                "HH:mm:ss",
                                CultureInfo.CurrentCulture),
                            theAction,
                            theURI,
                            Environment.NewLine)
                        );
                }
            }
        }

        private static bool IsURI(string theCandidate)
        {
            Uri theResultingURI = new Uri(theCandidate);

            return !theResultingURI.Scheme.Equals("file");
        }

        private void InitialiseAll()
        {
            for (int i = 0; i < this._MainView.gridDownloads.Rows.Count; i++)
            {
                IDownloadAgent thisAgent =
                    this._MainView.gridDownloads.Rows[i].Tag as IDownloadAgent;

                if (null != thisAgent)
                {
                    this.InitialiseDownload(thisAgent);
                }
            }

            lock (this._LockObject)
            {
                if (this._ActiveInitialiseCount.Equals(0) &&
                    !this._MainView.Status.Equals(MainViewStatus.Started))
                {
                    this.UpdateControlButtons();
                }
            }
        }
        
        private void PlayAll()
        {
            this._MainView.buttonReinitialise.Enabled = false;

            for (int i = 0; i < this._MainView.gridDownloads.Rows.Count; i++)
            {
                IDownloadAgent thisAgent =
                    this._MainView.gridDownloads.Rows[i].Tag as IDownloadAgent;

                if (null != thisAgent)
                {
                    this.StartDownload(thisAgent);
                }
            }

            lock (this._LockObject)
            {
                if (this._ActiveDownloadCount.Equals(0))
                {
                    this._MainView.Status = MainViewStatus.Stopped;

                    this.UpdateControlButtons();
                }
            }
        }

        private void PauseAll()
        {
            for (int i = 0; i < this._MainView.gridDownloads.Rows.Count; i++)
            {
                IDownloadAgent thisAgent =
                    this._MainView.gridDownloads.Rows[i].Tag as IDownloadAgent;

                if (null != thisAgent)
                {
                    if (thisAgent.Status.Equals(DownloadAgentStatus.Starting) ||
                        thisAgent.Status.Equals(DownloadAgentStatus.Downloading))
                    {
                        thisAgent.Status = DownloadAgentStatus.Paused;
                    }
                }
            }

            this.UpdateTitleText("RapidSpider - Paused");
            this.UpdateNotifyIconText("RapidSpider - Paused");
        }

        private void StopAll()
        {
            for (int i = 0; i < this._MainView.gridDownloads.Rows.Count; i++)
            {
                IDownloadAgent thisAgent =
                    this._MainView.gridDownloads.Rows[i].Tag as IDownloadAgent;

                if (null != thisAgent)
                {
                    if (thisAgent.Status.Equals(DownloadAgentStatus.Starting) ||
                        thisAgent.Status.Equals(DownloadAgentStatus.Downloading))
                    {
                        thisAgent.Status = DownloadAgentStatus.Cancelled;
                    }
                }
            }

            this.UpdateTitleText("RapidSpider");
            this.UpdateNotifyIconText("RapidSpider");
        }

        public void UpdateReinitialiseButton(
            IDownloadAgent theInstance)
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this._MainView.InvokeRequired)
            {
                this._MainView.Invoke(
                    new UpdateReinitialiseButtonHandler(this.UpdateReinitialiseButton),
                    theInstance);
            }
            else
            {
                if (!theInstance.Status.Equals(DownloadAgentStatus.Initialised) &&
                    !theInstance.Status.Equals(DownloadAgentStatus.FileDeleted) &&
                    !theInstance.Status.Equals(DownloadAgentStatus.FileNotFound) &&
                    !this._MainView.buttonReinitialise.Enabled)
                {
                    this._MainView.buttonReinitialise.Enabled = true;
                }
            }
        }

        public void UpdateNotifyIconText(
            string theCaption)
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this._MainView.InvokeRequired)
            {
                this._MainView.Invoke(
                    new UpdateNotifyIconTextHandler(this.UpdateNotifyIconText),
                    theCaption);
            }
            else
            {
                this._MainView.NotifyIcon.Text =
                    theCaption;
            }
        }

        public void UpdateTitleText(
            string theCaption)
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this._MainView.InvokeRequired)
            {
                this._MainView.Invoke(
                    new UpdateTitleTextHandler(this.UpdateTitleText),
                    theCaption);
            }
            else
            {
                this._MainView.Text =
                    theCaption;
            }
        }

        public void UpdateStatusBar()
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this._MainView.InvokeRequired)
            {
                this._MainView.Invoke(
                    new UpdateStatusBarHandler(this.UpdateStatusBar));
            }
            else
            {
                decimal theNumberDownloading = 0;
                decimal theNumberActive = 0;
                decimal theTotalPercentage = 0;

                foreach (DataGridViewRow thisRow in this._MainView.gridDownloads.Rows)
                {
                    IDownloadAgent thisAgent = thisRow.Tag as IDownloadAgent;

                    if (null != thisAgent)
                    {
                        if (thisAgent.Status.Equals(DownloadAgentStatus.Starting) ||
                            thisAgent.Status.Equals(DownloadAgentStatus.Downloading))
                        {
                            ++theNumberDownloading;
                            theTotalPercentage += thisAgent.DownloadedPercentage;
                        }
                        ++theNumberActive;
                    }
                }

                if (theNumberActive.Equals(0))
                {
                    this.UpdateTitleText("RapidSpider");
                    this.UpdateNotifyIconText("RapidSpider");
                }
                else if (!theNumberDownloading.Equals(0))
                {
                    this.UpdateTitleText(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            "RapidSpider - {0}% of {1} file{2} downloaded; {3} pending",
                            Math.Round(theTotalPercentage / theNumberDownloading, 0),
                            theNumberDownloading,
                            theNumberDownloading.Equals(1) ? String.Empty : "s",
                            theNumberActive - theNumberDownloading));
                    this.UpdateNotifyIconText(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            "RapidSpider - {0}% of {1} file{2} downloaded; {3} pending",
                            Math.Round(theTotalPercentage / theNumberDownloading, 0),
                            theNumberDownloading,
                            theNumberDownloading.Equals(1) ? String.Empty : "s",
                            theNumberActive - theNumberDownloading));
                }
            }
        }

        public void UpdateControlButtons()
        {
            // InvokeRequired compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this._MainView.InvokeRequired)
            {
                this._MainView.Invoke(
                    new UpdateControlButtonsHandler(this.UpdateControlButtons));
            }
            else
            {
                this._MainView.buttonPlayAll.Enabled =
                    this._MainView.menuitemPlayAll.Enabled =
                        this._MainView.gridDownloads.Rows.Count > 0;
            }
        }

        private delegate void UpdateNotifyIconTextHandler(string theCaption);
        private delegate void UpdateStatusBarHandler();
        private delegate void UpdateTitleTextHandler(string theCaption);
        private delegate void UpdateLogFileHandler(string theAction, string theLog);
        private delegate void UpdateReinitialiseButtonHandler(IDownloadAgent theInstance);
        private delegate void UpdateControlButtonsHandler();
        #endregion
        #endregion
    }
}
